/**
 * Util.java
 * Copyright phonemap.com
 *
 * Licensed under the phonemap.com License, Version 1.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.phonemap.com/licenses/LICENSE-1.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.phonemap.common;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.EntityNotFoundException;
import com.google.appengine.api.datastore.FetchOptions;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.PreparedQuery;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Query.FilterOperator;
import com.google.appengine.api.datastore.Query.FilterPredicate;
import com.google.appengine.api.datastore.Query.SortDirection;
import com.google.appengine.api.datastore.Transaction;
import com.google.appengine.api.memcache.MemcacheService;
import com.google.appengine.api.memcache.MemcacheServiceFactory;
import com.phonemap.dto.PositionDTO;

/**
 * This is the utility class for all servlets. It provides method for inserting,
 * deleting, searching the entity from data store. Also contains method for
 * displaying the entity in JSON format.
 * 
 */
public final class Util {

	/** The logger. */
	private static Logger logger = Logger.getLogger(Util.class
			.getCanonicalName());

	/** The datastore. */
	private static DatastoreService datastore = DatastoreServiceFactory
			.getDatastoreService();

	/** The keycache. */
	private static MemcacheService keycache = MemcacheServiceFactory
			.getMemcacheService();

	/**
	 * Instantiates a new util.
	 */
	private Util() {

	}

	/**
	 * Add the entity to cache and also to the datastore.
	 * 
	 * @param entity
	 *            : entity to be persisted
	 */
	public static void persistEntity(final Entity entity) {
		logger.log(Level.INFO, "Saving entity");
		Key key = entity.getKey();
		Transaction txn = datastore.beginTransaction();
		try {
			datastore.put(entity);
			txn.commit();
		} finally {
			if (txn.isActive()) {
				txn.rollback();
			} else {
				addToCache(key, entity);
			}
		}
	}

	/**
	 * Delete the entity from persistent store represented by the key Also
	 * delete it from cache.
	 * 
	 * @param key
	 *            : key to delete the entity from the persistent store
	 */
	public static void deleteEntity(final Key key) {
		logger.log(Level.INFO, "Deleting entity");
		Transaction txn = datastore.beginTransaction();
		try {
			datastore.delete(key);
			txn.commit();
		} finally {
			if (txn.isActive()) {
				txn.rollback();
			} else {
				deleteFromCache(key);
			}
		}
	}

	/**
	 * Delete the entities represented by a list of keys Delete the entitites
	 * from cache also.
	 * 
	 * @param keys
	 *            : keys for the entities that are to be deleted
	 */
	public static void deleteEntity(final List<Key> keys) {
		datastore.delete(new Iterable<Key>() {
			public Iterator<Key> iterator() {
				return keys.iterator();
			}
		});
		deleteFromCache(keys);
	}

	/**
	 * Search and return the entity from the cache . If absent , search the
	 * datastore.
	 * 
	 * @param key
	 *            : key to find the entity
	 * @return entity
	 */
	public static Entity findEntity(final Key key) {
		logger.log(Level.INFO, "Search the entity");
		try {
			Entity entity = getFromCache(key);
			if (entity != null) {
				return entity;
			}
			return datastore.get(key);
		} catch (EntityNotFoundException e) {
			return null;
		}
	}

	/**
	 * * Search entities based on search criteria.
	 * 
	 * @param kind
	 *            the kind
	 * @param searchBy
	 *            : Searching Criteria (Property)
	 * @param searchFor
	 *            : Searching Value (Property Value)
	 * @return List all entities of a kind from the cache or datastore (if not
	 *         in cache) with the specified properties
	 */
	public static Iterable<Entity> listEntities(final String kind,
			final String searchBy, final String searchFor) {
		logger.log(Level.INFO, "Search entities based on search criteria");
		Query query = new Query(kind);
		if (searchFor != null && !"".equals(searchFor)) {
			query.setFilter(new FilterPredicate(searchBy, FilterOperator.EQUAL,
					searchFor));
		}
		PreparedQuery pq = datastore.prepare(query);
		return pq.asIterable();
	}
	
	/**
	 * List entities.
	 *
	 * @param kind the kind
	 * @param query the query
	 * @return the iterable
	 */
	public static Iterable<Entity> listEntities(final String kind,
			final Query query) {
		logger.log(Level.INFO, "Search entities based on search criteria");
		PreparedQuery pq = datastore.prepare(query);
		return pq.asIterable();
	}
	
	
	/**
	 * List entities.
	 *
	 * @param kind the kind
	 * @param query the query
	 * @param offset the offset
	 * @return the iterable
	 */
	public static Iterable<Entity> listEntities(final String kind,
			final Query query, final int offset) {
		logger.log(Level.INFO, "Search entities based on search criteria");
		PreparedQuery pq = datastore.prepare(query);
		return pq.asIterable(FetchOptions.Builder.withOffset(offset).limit(
				AppConstant.MAX_PAGING));
	}

	/**
	 * List entities.
	 * 
	 * @param kind
	 *            the kind
	 * @param searchByNull
	 *            the search by null
	 * @return the iterable
	 */
	public static Iterable<Entity> listEntities(final String kind,
			final String searchByNull) {
		logger.log(Level.INFO, "Search entities based on search criteria");
		Query query = new Query(kind);
		query.setFilter(new FilterPredicate(searchByNull, FilterOperator.EQUAL,
				null));
		PreparedQuery pq = datastore.prepare(query);
		return pq.asIterable();
	}

	/**
	 * * Search entities based on search criteria.
	 * 
	 * @param kind
	 *            the kind
	 * @param searchBy
	 *            : Searching Criteria (Property)
	 * @param searchFor
	 *            : Searching Value (Property Value)
	 * @param offset
	 *            the offset
	 * @return List all entities of a kind from the cache or datastore (if not
	 *         in cache) with the specified properties
	 */
	public static Iterable<Entity> getEntities(final String kind,
			final String searchBy, final String searchFor, final int offset) {
		logger.log(Level.INFO, "Search entities based on search criteria");
		Query query = new Query(kind);
		if (searchFor != null && !"".equals(searchFor)) {
			query.setFilter(new FilterPredicate(searchBy, FilterOperator.EQUAL,
					searchFor));
		}
		PreparedQuery pq = datastore.prepare(query);
		return pq.asIterable(FetchOptions.Builder.withOffset(offset).limit(
				AppConstant.MAX_PAGING));
	}

	/**
	 * * Search entities based on search criteria.
	 * 
	 * @param kind
	 *            the kind
	 * @param searchBy
	 *            : Searching Criteria (Property)
	 * @param searchFor
	 *            : Searching Value (Property Value)
	 * @param offset
	 *            the offset
	 * @param operator
	 *            the operator
	 * @return List all entities of a kind from the cache or datastore (if not
	 *         in cache) with the specified properties
	 */
	public static List<Entity> getEntities(final String kind,
			final String searchBy, final String searchFor, final int offset,
			final FilterOperator operator) {
		logger.log(Level.INFO, "Search entities based on search criteria");
		Query query = new Query(kind);
		if (searchFor != null && !"".equals(searchFor)) {
			query.setFilter(new FilterPredicate(searchBy, operator, searchFor));
		}
		PreparedQuery pq = datastore.prepare(query);
		return pq.asList(FetchOptions.Builder.withOffset(
				offset * AppConstant.MAX_PAGING).limit(AppConstant.MAX_PAGING));

	}

	/**
	 * Get the list of children from a parent key in the entity group.
	 * 
	 * @param kind
	 *            : the entity kind of the children that is to be searched for
	 * @param ancestor
	 *            : the parent key of the entity group where we need to search
	 * @return iterable with all children of the parent of the specified kind
	 */
	public static Iterable<Entity> listChildren(final String kind,
			final Key ancestor) {
		logger.log(Level.INFO, "Search entities based on parent");
		Query query = new Query(kind);
		query.setAncestor(ancestor);
		query.setFilter(new FilterPredicate(Entity.KEY_RESERVED_PROPERTY,
				FilterOperator.GREATER_THAN, ancestor));
		PreparedQuery pq = datastore.prepare(query);
		return pq.asIterable();
	}

	/**
	 * List children.
	 * 
	 * @param kind
	 *            the kind
	 * @param ancestor
	 *            the ancestor
	 * @param sortDirection
	 *            the sort direction
	 * @return the iterable
	 */
	public static Iterable<Entity> listChildren(final String kind,
			final Key ancestor, final SortDirection sortDirection) {
		logger.log(Level.INFO, "Search entities based on parent");
		Query query = new Query(kind);
		query.setAncestor(ancestor);
		query.setFilter(new FilterPredicate(Entity.KEY_RESERVED_PROPERTY,
				FilterOperator.GREATER_THAN, ancestor));
		query.addSort(Entity.KEY_RESERVED_PROPERTY, sortDirection);
		PreparedQuery pq = datastore.prepare(query);
		return pq.asIterable();
	}

	/**
	 * List children.
	 * 
	 * @param query
	 *            the query
	 * @param offset
	 *            the offset
	 * @return the list
	 */
	public static List<Entity> listChildren(final Query query, final int offset) {
		logger.log(Level.INFO, "Search entities based on parent");
		PreparedQuery pq = datastore.prepare(query);
		return pq.asList(FetchOptions.Builder.withOffset(
				offset * AppConstant.MAX_PAGING).limit(AppConstant.MAX_PAGING));
	}

	/**
	 * Get the list of keys of all children for a given entity kind in a given
	 * entity group represented by the parent key.
	 * 
	 * @param kind
	 *            : Entity kind of the children that needs to be searched for
	 * @param ancestor
	 *            : Parent key of the entity group that needs to be searched for
	 * @return an iterable with keys of children
	 */
	public static Iterable<Entity> listChildKeys(final String kind,
			final Key ancestor) {
		logger.log(Level.INFO, "Search entities based on parent");
		Query query = new Query(kind);
		query.setAncestor(ancestor).setKeysOnly();
		query.setFilter(new FilterPredicate(Entity.KEY_RESERVED_PROPERTY,
				FilterOperator.GREATER_THAN, ancestor));
		PreparedQuery pq = datastore.prepare(query);
		return pq.asIterable();
	}

	/**
	 * List the entities in JSON format.
	 * 
	 * @param entities
	 *            entities to return as JSON strings
	 * @return the string
	 */
	public static String writeJSON(final Iterable<Entity> entities) {
		logger.log(Level.INFO, "creating JSON format object");
		StringBuilder sb = new StringBuilder();
		int i = 0;
		sb.append("{\"data\": [");
		// List<Entity> userList = new ArrayList<Entity>();
		// for (Entity entity : entities) {
		// userList.add(entity);
		// }
		for (Entity result : entities) {
			Map<String, Object> properties = result.getProperties();
			sb.append("{");
			if (result.getKey().getName() == null) {
				sb.append("\"name\" : \"" + result.getKey().getId() + "\",");
			} else {
				sb.append("\"name\" : \"" + result.getKey().getName() + "\",");
			}
			for (String key : properties.keySet()) {
				sb.append("\"" + key + "\" : \"" + properties.get(key) + "\",");
			}
			sb.deleteCharAt(sb.lastIndexOf(","));
			sb.append("},");
			i++;
		}
		if (i > 0) {
			sb.deleteCharAt(sb.lastIndexOf(","));
		}
		sb.append("]}");
		return sb.toString();
	}

	/**
	 * Write position json.
	 * 
	 * @param positions
	 *            the positions
	 * @return the string
	 */
	public static String writePositionJSON(final List<PositionDTO> positions) {
		logger.log(Level.INFO, "creating JSON format object");
		StringBuilder sb = new StringBuilder();
		int i = 0;
		sb.append("{\"data\": [");
		// List<Entity> userList = new ArrayList<Entity>();
		// for (Entity entity : entities) {
		// userList.add(entity);
		// }
		for (PositionDTO dto : positions) {
			sb.append(dto.toString());
			sb.append(",");
			i++;
		}
		if (i > 0) {
			sb.deleteCharAt(sb.lastIndexOf(","));
		}
		sb.append("]}");
		return sb.toString();
	}

	/**
	 * Write position json.
	 *
	 * @param positions the positions
	 * @param status the status
	 * @param message the message
	 * @return the string
	 */
	public static String writePositionJSON(final List<PositionDTO> positions,
			final String status, final String message) {
		logger.log(Level.INFO, "creating JSON format object");
		StringBuilder sb = new StringBuilder();
		int i = 0;
		sb.append("{\"status\":\"" + status + "\",");
		sb.append("\"message\":\"" + message + "\",");
		sb.append("{\"data\": [");
		// List<Entity> userList = new ArrayList<Entity>();
		// for (Entity entity : entities) {
		// userList.add(entity);
		// }
		for (PositionDTO dto : positions) {
			sb.append(dto.toString());
			sb.append(",");
			i++;
		}
		if (i > 0) {
			sb.deleteCharAt(sb.lastIndexOf(","));
		}
		sb.append("]}");
		return sb.toString();
	}

	/**
	 * List the entities in JSON format.
	 * 
	 * @param entities
	 *            entities to return as JSON strings
	 * @param status
	 *            the status
	 * @param message
	 *            the message
	 * @return the string
	 */
	public static String writeWSJSON(final Iterable<Entity> entities,
			final String status, final String message) {
		logger.log(Level.INFO, "creating JSON format object");
		StringBuilder sb = new StringBuilder();
		int i = 0;
		sb.append("{\"status\":\"" + status + "\",");
		sb.append("\"message\":\"" + message + "\",");
		sb.append("\"data\": [");
		for (Entity result : entities) {
			Map<String, Object> properties = result.getProperties();
			sb.append("{");
			if (result.getKey().getName() == null) {
				sb.append("\"name\" : \"" + result.getKey().getId() + "\",");
			} else {
				sb.append("\"name\" : \"" + result.getKey().getName() + "\",");
			}
			for (String key : properties.keySet()) {
				sb.append("\"" + key + "\" : \"" + properties.get(key) + "\",");
			}
			sb.deleteCharAt(sb.lastIndexOf(","));
			sb.append("},");
			i++;
		}
		if (i > 0) {
			sb.deleteCharAt(sb.lastIndexOf(","));
		}
		sb.append("]}");
		return sb.toString();
	}

	/**
	 * List the entities in JSON format.
	 * 
	 * @param entities
	 *            entities to return as JSON strings
	 * @return the string
	 */
	public static String writeJSON(final List<Entity> entities) {
		logger.log(Level.INFO, "creating JSON format object");
		StringBuilder sb = new StringBuilder();
		int i = 0;
		sb.append("{\"data\": [");
		for (Entity result : entities) {
			Map<String, Object> properties = result.getProperties();
			sb.append("{");
			if (result.getKey().getName() == null) {
				sb.append("\"name\" : \"" + result.getKey().getId() + "\",");
			} else {
				sb.append("\"name\" : \"" + result.getKey().getName() + "\",");
			}
			for (String key : properties.keySet()) {
				sb.append("\"" + key + "\" : \"" + properties.get(key) + "\",");
			}
			sb.deleteCharAt(sb.lastIndexOf(","));
			sb.append("},");
			i++;
		}
		if (i > 0) {
			sb.deleteCharAt(sb.lastIndexOf(","));
		}
		sb.append("]}");
		return sb.toString();
	}

	/**
	 * List the entities in JSON format.
	 * 
	 * @param paramName
	 *            the param name
	 * @param paramValue
	 *            the param value
	 * @return the string
	 */
	public static String writeJSON(final String paramName,
			final String paramValue) {
		logger.log(Level.INFO, "creating JSON format object");
		StringBuilder sb = new StringBuilder();
		sb.append("{\"data\": [{");

		if (paramName != null) {
			sb.append("\"" + paramName + "\":\"" + paramValue + "\"");
		}

		sb.append("}]}");
		return sb.toString();
	}

	/**
	 * List the entities in JSON format.
	 * 
	 * @param entities
	 *            entities to return as JSON strings
	 * @param status
	 *            the status
	 * @param message
	 *            the message
	 * @return the string
	 */
	public static String writeWSJSON(final List<Entity> entities,
			final String status, final String message) {
		logger.log(Level.INFO, "creating JSON format object for web service");
		StringBuilder sb = new StringBuilder();
		int i = 0;
		sb.append("{\"status\":\"" + status + "\",");
		sb.append("\"message\":\"" + message + "\",");
		sb.append("\"data\": [");
		for (Entity result : entities) {
			Map<String, Object> properties = result.getProperties();
			sb.append("{");
			if (result.getKey().getName() == null) {
				sb.append("\"name\" : \"" + result.getKey().getId() + "\",");
			} else {
				sb.append("\"name\" : \"" + result.getKey().getName() + "\",");
			}
			for (String key : properties.keySet()) {
				// if (key.equalsIgnoreCase("photoData")) {
				// sb.append("\"" + key + "\" : \"" + "Image" + "\",");
				// continue;
				// }
				sb.append("\"" + key + "\" : \"" + properties.get(key) + "\",");
			}
			sb.deleteCharAt(sb.lastIndexOf(","));
			sb.append("},");
			i++;
		}
		if (i > 0) {
			sb.deleteCharAt(sb.lastIndexOf(","));
		}
		sb.append("]}");
		return sb.toString();
	}

	/**
	 * List the entities in JSON format.
	 * 
	 * @param entity
	 *            the entity
	 * @param status
	 *            the status
	 * @param message
	 *            the message
	 * @return the string
	 */
	public static String writeWSJSON(final Entity entity, final String status,
			final String message) {
		logger.log(Level.INFO, "creating JSON format object for web service");
		StringBuilder sb = new StringBuilder();
		sb.append("{\"status\":\"" + status + "\",");
		sb.append("\"message\":\"" + message + "\",");
		sb.append("\"data\": [");
		// add entity
		if (entity != null) {
			Map<String, Object> properties = entity.getProperties();
			sb.append("{");
			if (entity.getKey().getName() == null) {
				sb.append("\"name\" : \"" + entity.getKey().getId() + "\",");
			} else {
				sb.append("\"name\" : \"" + entity.getKey().getName() + "\",");
			}
			for (String key : properties.keySet()) {
				// if (key.equalsIgnoreCase("photoData")) {
				// sb.append("\"" + key + "\" : \"" + "Image" + "\",");
				// continue;
				// }
				sb.append("\"" + key + "\" : \"" + properties.get(key) + "\",");
			}
			sb.deleteCharAt(sb.lastIndexOf(","));
			sb.append("}");
		}

		sb.append("]}");
		return sb.toString();
	}

	/**
	 * Retrieves Parent and Child entities into JSON String.
	 * 
	 * @param entities
	 *            : List of parent entities
	 * @param parentKind
	 *            : Entity type for Parent
	 * @return JSON string
	 */
	public static String writeJSON(final Iterable<Entity> entities,
			final String parentKind) {
		logger.log(Level.INFO,
				"creating JSON format object for parent child relation");
		StringBuilder sb = new StringBuilder();
		int i = 0;
		sb.append("{\"data\": [");
		for (Entity result : entities) {
			Map<String, Object> properties = result.getProperties();
			sb.append("{");
			if (result.getKey().getName() == null) {
				sb.append("\"name\" : \"" + result.getKey().getId() + "\",");
			} else {
				sb.append("\"name\" : \"" + result.getKey().getName() + "\",");
			}

			for (String key : properties.keySet()) {
				sb.append("\"" + key + "\" : \"" + properties.get(key) + "\",");
				if (key.equalsIgnoreCase("userId")) {
					Entity parent = findEntity(KeyFactory.createKey(parentKind,
							properties.get(key).toString()));
					Map<String, Object> parentProperties = parent
							.getProperties();
					for (String pkey : parentProperties.keySet()) {
						sb.append("\"" + pkey + "\" : \""
								+ parentProperties.get(pkey) + "\",");
					}
				}

			}

			sb.deleteCharAt(sb.lastIndexOf(","));
			sb.append("},");
			i++;
		}
		if (i > 0) {
			sb.deleteCharAt(sb.lastIndexOf(","));
		}
		sb.append("]}");
		return sb.toString();
	}

	/**
	 * Retrieves Parent and Child entities into JSON String.
	 * 
	 * @param entities
	 *            : List of parent entities
	 * @param parentKind
	 *            : Entity type for Parent
	 * @param status
	 *            the status
	 * @param message
	 *            the message
	 * @return JSON string
	 */
	public static String writeWSJSON(final Iterable<Entity> entities,
			final String parentKind, final String status, final String message) {
		logger.log(Level.INFO,
				"creating JSON format object for parent child relation for web service");
		StringBuilder sb = new StringBuilder();
		int i = 0;
		sb.append("{\"status\":\"" + status + "\",");
		sb.append("\"message\":\"" + message + "\",");
		sb.append("\"data\": [");
		for (Entity result : entities) {
			Map<String, Object> properties = result.getProperties();
			sb.append("{");
			if (result.getKey().getName() == null) {
				sb.append("\"name\" : \"" + result.getKey().getId() + "\",");
			} else {
				sb.append("\"name\" : \"" + result.getKey().getName() + "\",");
			}

			for (String key : properties.keySet()) {
				sb.append("\"" + key + "\" : \"" + properties.get(key) + "\",");
				if (key.equalsIgnoreCase("userId")) {
					Entity parent = findEntity(KeyFactory.createKey(parentKind,
							properties.get(key).toString()));
					Map<String, Object> parentProperties = parent
							.getProperties();
					for (String pkey : parentProperties.keySet()) {
						sb.append("\"" + pkey + "\" : \""
								+ parentProperties.get(pkey) + "\",");
					}
				}

			}

			sb.deleteCharAt(sb.lastIndexOf(","));
			sb.append("},");
			i++;
		}
		if (i > 0) {
			sb.deleteCharAt(sb.lastIndexOf(","));
		}
		sb.append("]}");
		return sb.toString();
	}

	/**
	 * Retrieves Parent and Child entities into JSON String.
	 * 
	 * @param entities
	 *            : List of parent entities
	 * @param childKind
	 *            : Entity type for Child
	 * @param fkName
	 *            : foreign-key to the parent in the child entity
	 * @return JSON string
	 */
	public static String writeJSON(final Iterable<Entity> entities,
			final String childKind, final String fkName) {
		logger.log(Level.INFO,
				"creating JSON format object for parent child relation");
		StringBuilder sb = new StringBuilder();
		int i = 0;
		sb.append("{\"data\": [");
		for (Entity result : entities) {
			Map<String, Object> properties = result.getProperties();
			sb.append("{");
			if (result.getKey().getName() == null) {
				sb.append("\"name\" : \"" + result.getKey().getId() + "\",");
			} else {
				sb.append("\"name\" : \"" + result.getKey().getName() + "\",");
			}
			for (String key : properties.keySet()) {
				sb.append("\"" + key + "\" : \"" + properties.get(key) + "\",");
			}
			Iterable<Entity> child = listEntities(childKind, fkName,
					String.valueOf(result.getKey().getId()));
			for (Entity en : child) {
				for (String key : en.getProperties().keySet()) {
					sb.append("\"" + key + "\" : \""
							+ en.getProperties().get(key) + "\",");
				}
			}
			sb.deleteCharAt(sb.lastIndexOf(","));
			sb.append("},");
			i++;
		}
		if (i > 0) {
			sb.deleteCharAt(sb.lastIndexOf(","));
		}
		sb.append("]}");
		return sb.toString();
	}

	/**
	 * Retrieves Parent and Child entities into JSON String.
	 * 
	 * @param entities
	 *            : List of parent entities
	 * @param childKind
	 *            : Entity type for Child
	 * @param fkName
	 *            : foreign-key to the parent in the child entity
	 * @param status
	 *            the status
	 * @param message
	 *            the message
	 * @return JSON string
	 */
	public static String writeWSJSON(final Iterable<Entity> entities,
			final String childKind, final String fkName, final String status,
			final String message) {
		logger.log(Level.INFO,
				"creating JSON format object for parent child relation");
		StringBuilder sb = new StringBuilder();
		int i = 0;
		sb.append("{\"status\":\"" + status + "\",");
		sb.append("\"message\":\"" + message + "\",");
		sb.append("\"data\": [");
		for (Entity result : entities) {
			Map<String, Object> properties = result.getProperties();
			sb.append("{");
			if (result.getKey().getName() == null) {
				sb.append("\"name\" : \"" + result.getKey().getId() + "\",");
			} else {
				sb.append("\"name\" : \"" + result.getKey().getName() + "\",");
			}
			for (String key : properties.keySet()) {
				sb.append("\"" + key + "\" : \"" + properties.get(key) + "\",");
			}
			Iterable<Entity> child = listEntities(childKind, fkName,
					String.valueOf(result.getKey().getId()));
			for (Entity en : child) {
				for (String key : en.getProperties().keySet()) {
					sb.append("\"" + key + "\" : \""
							+ en.getProperties().get(key) + "\",");
				}
			}
			sb.deleteCharAt(sb.lastIndexOf(","));
			sb.append("},");
			i++;
		}
		if (i > 0) {
			sb.deleteCharAt(sb.lastIndexOf(","));
		}
		sb.append("]}");
		return sb.toString();
	}

	/**
	 * Adds the entity to cache.
	 * 
	 * @param key
	 *            : key of the entity
	 * @param entity
	 *            : Entity being added
	 */
	public static void addToCache(final Key key, final Entity entity) {
		logger.log(Level.INFO, "Adding entity to cache");
		keycache.put(key, entity);
	}

	/**
	 * Delete the entity from cache.
	 * 
	 * @param key
	 *            : Key of the entity that needs to be deleted
	 */
	public static void deleteFromCache(final Key key) {
		logger.log(Level.INFO, "Deleting entity from cache");
		keycache.delete(key);
	}

	/**
	 * Delete entities based on a set of keys.
	 * 
	 * @param keys
	 *            : list of keys for the entities that are to be deleted
	 */
	public static void deleteFromCache(final List<Key> keys) {
		keycache.deleteAll(keys);
	}

	/**
	 * Search for an entity based on key in the cache.
	 * 
	 * @param key
	 *            : key of the entity that is searched for
	 * @return the entity
	 */
	public static Entity getFromCache(final Key key) {
		logger.log(Level.INFO, "Searching entity in cache");
		return (Entity) keycache.get(key);
	}

	/**
	 * Utility method to send the error back to UI.
	 * 
	 * @param ex
	 *            the ex
	 * @return the error response
	 * @throws IOException
	 *             Signals that an I/O exception has occurred.
	 */
	public static String getErrorResponse(final Exception ex)
			throws IOException {
		return "Error:" + ex.toString();
	}

	/**
	 * Utility method to get the datastore service in entities.
	 * 
	 * @return datastore
	 */
	public static DatastoreService getDatastoreServiceInstance() {
		return datastore;
	}

	/**
	 * Gets the max entities.
	 * 
	 * @param kind
	 *            the kind
	 * @param searchBy
	 *            the search by
	 * @param searchFor
	 *            the search for
	 * @param sortBy
	 *            the sort by
	 * @return the max entities
	 */
	public static Entity getMaxEntities(final String kind,
			final String searchBy, final String searchFor, final String sortBy) {
		logger.log(Level.INFO, "Search entities based on search criteria");
		Query query = new Query(kind);
		if (searchFor != null && !"".equals(searchFor)) {
			query.setFilter(new FilterPredicate(searchBy, FilterOperator.EQUAL,
					searchFor));
		}
		if (sortBy != null && !"".equals(sortBy)) {
			query.addSort(sortBy, SortDirection.DESCENDING);
		}
		PreparedQuery pq = datastore.prepare(query);

		Iterable<Entity> results = pq.asIterable();
		List<Entity> entity = new ArrayList<Entity>();
		for (Entity e : results) {
			if (e != null) {
				entity.add(e);
			}
		}
		if (!entity.isEmpty()) {
			return (Entity) entity.remove(0);
		}
		return null;

	}

}